﻿using Emgu.CV;
using Emgu.CV.Structure;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace ImageMerge
{
    public unsafe class ImgMerging
    {    
        [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = true)]
        public static extern void CopyMemory(byte* Dest, byte* src, int Length);

        private static string _strError = "";
        public static string StrErrorInfo
        {
            get
            {
                return _strError;
            }
        }

        public static bool Invert(Bitmap b)
        {
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
            ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - b.Width * 3;
                int nWidth = b.Width * 3;
                for (int y = 0; y < b.Height; ++y)
                {
                    for (int x = 0; x < nWidth; ++x)
                    {
                        p[0] = (byte)(255 - p[0]);
                        ++p;
                    }
                    p += nOffset;
                }
            }
            b.UnlockBits(bmData);
            return true;
        }
    
        public static Bitmap SetBmpOpacity(Bitmap srcImage, int percent)
        {
            int iwidth = srcImage.Width;
            int iheight = srcImage.Height;
            float fp = percent / 100f;
            Bitmap newImage = new Bitmap(iwidth, iheight, PixelFormat.Format32bppArgb);
            float[][] colormatrix =
            {
                new float[]{1,0,0,0,0},//代表了R
                new float[]{0,1,0,0,0},//代表了G
                new float[]{0,0,1,0,0},//代表了B
                new float[]{0,0,0,fp,0},//代表了A
                new float[]{0,0,0,0,1}
            };
            using (Graphics g = Graphics.FromImage(newImage))
            {
                ColorMatrix cm = new ColorMatrix(colormatrix);
                ImageAttributes imageAtt = new ImageAttributes();
                imageAtt.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
                g.DrawImage(srcImage, new Rectangle(0, 0, iwidth, iheight), 0, 0, iwidth, iheight, GraphicsUnit.Pixel, imageAtt);
            }
            return newImage;
        }

        /// <summary>
        /// 调整颜色分量
        /// </summary>
        /// <param name="b"></param>
        /// <param name="devGreen"></param>
        /// <param name="devRed"></param>
        /// <param name="devBlue"></param>
        public static void AdjustColorWithSkinRecognition(Bitmap b, int devGreen = 0, int devRed = 0, int devBlue = 0)
        {
            if(b == null || b.Size.IsEmpty || Math.Abs(devBlue) > 255 || Math.Abs(devRed) > 255 || Math.Abs(devGreen) > 255)
            {
                return;
            }
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            byte B, G, R;
            byte minVal = (byte)250;
            byte dr = (byte)Math.Abs(devRed);
            byte dg = (byte)Math.Abs(devGreen);
            byte db = (byte)Math.Abs(devBlue);
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - b.Width * 3;
                double r, g, Fupr, Flor, Wrg;
                for (int y = 0; y < b.Height; ++y)
                {
                    for (int x = 0; x < b.Width; ++x, p += 3)
                    {
                        B = p[0];
                        G = p[1];
                        R = p[2];

                        if (B > minVal && G > minVal && R > minVal)
                        {
                            continue;
                        }

                        if (R + G + B == 0)
                        {
                            r = 0;
                            g = 0;
                        }
                        else
                        {
                            r = (R / (R + G + B));
                            g = (G / (R + G + B));
                        }
                        Fupr = (1.0743 * r + 0.1452 - 1.3767 * r * r);
                        Flor = (0.5601 * r + 0.1766 - 0.776 * r * r);
                        Wrg = (r - 0.33) * (r - 0.33) + (g - 0.33) * (g - 0.33);
                        if ((R - G >= 45) && ((R > G) && (G > B)) && (Fupr > g) && (Wrg >= 0.0004))
                        {
                            //P[0] = (byte)B;
                            //P[1] = (byte)G;
                            //P[2] = (byte)R;
                            if (devBlue < 0)
                            {
                                if (B < db)
                                {
                                    p[0] = 0;
                                }
                                else
                                {
                                    p[0] -= db;
                                }
                            }
                            if (devBlue > 0)
                            {
                                p[0] = (B + db) > 255 ? (byte)255 : (byte)(B + db);
                            }

                            if (devGreen < 0)
                            {
                                if (G < dg)
                                {
                                    p[1] = 0;
                                }
                                else
                                {
                                    p[1] -= dg;
                                }
                            }
                            if (devGreen > 0)
                            {
                                p[1] = (G + dg) > 255 ? (byte)255 : (byte)(G + dg);
                            }

                            if (devRed < 0)
                            {
                                if (R < dr)
                                {
                                    p[2] = 0;
                                }
                                else
                                {
                                    p[2] -= dr;
                                }
                            }
                            if (devRed > 0)
                            {
                                p[2] = (R + dr) > 255 ? (byte)255 : (byte)(R + dr);
                            }
                        }
                    }
                    p += nOffset;
                }
            }
            b.UnlockBits(bmData);
        }

        /// <summary>
        /// 调整颜色分量
        /// </summary>
        /// <param name="b"></param>
        /// <param name="devGreen"></param>
        /// <param name="devRed"></param>
        /// <param name="devBlue"></param>
        public static void AdjustColor(Bitmap b, int devGreen = 0, int devRed = 0, int devBlue = 0)
        {
            if (b == null || b.Size.IsEmpty || Math.Abs(devBlue) > 255 || Math.Abs(devRed) > 255 || Math.Abs(devGreen) > 255)
            {
                return;
            }
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;
            byte red, green, blue;
            byte minVal = (byte)250;
            byte dr = (byte)Math.Abs(devRed);
            byte dg = (byte)Math.Abs(devGreen);
            byte db = (byte)Math.Abs(devBlue);
            unsafe
            {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - b.Width * 3;
                for (int y = 0; y < b.Height; ++y)
                {
                    for (int x = 0; x < b.Width; ++x, p += 3)
                    {
                        blue = p[0];
                        green = p[1];
                        red = p[2];

                        if (blue > minVal && green > minVal && red > minVal)
                        {
                            continue;
                        }

                        if (devBlue < 0)
                        {
                            if (blue < db)
                            {
                                p[0] = 0;
                            }
                            else
                            {
                                p[0] -= db;
                            }
                        }
                        if (devBlue > 0)
                        {
                            p[0] = (blue + db) > 255 ? (byte)255 : (byte)(blue + db);
                        }

                        if (devGreen < 0)
                        {
                            if (green < dg)
                            {
                                p[1] = 0;
                            }
                            else
                            {
                                p[1] -= dg;
                            }
                        }
                        if (devGreen > 0)
                        {
                            p[1] = (green + dg) > 255 ? (byte)255 : (byte)(green + dg);
                        }

                        if (devRed < 0)
                        {
                            if (red < dr)
                            {
                                p[2] = 0;
                            }
                            else
                            {
                                p[2] -= dr;
                            }
                        }
                        if (devRed > 0)
                        {
                            p[2] = (red + dr) > 255 ? (byte)255 : (byte)(red + dr);
                        }
                    }
                    p += nOffset;
                }
            }
            b.UnlockBits(bmData);
        }

        public static Bitmap ToGrayByCV(Bitmap srcImage)
        {
            Image<Bgr, Byte> imageSource = new Image<Bgr, byte>(srcImage);
            Image<Gray, Byte> imageGrayscale = imageSource.Convert<Gray, Byte>();
            imageSource.Dispose();
            imageSource = null;
            return imageGrayscale.Bitmap;
        }

        public static Bitmap PTransparentAdjust(Bitmap src, int num)
        {
            try
            {
                int w = src.Width;
                int h = src.Height;
                Bitmap dstBitmap = new Bitmap(src.Width, src.Height, PixelFormat.Format32bppArgb);
                BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                unsafe
                {
                    byte* pIn = (byte*)srcData.Scan0.ToPointer();
                    byte* pOut = (byte*)dstData.Scan0.ToPointer();
                    byte* p;
                    int stride = srcData.Stride;
                    int r, g, b;
                    for (int y = 0; y < h; y++)
                    {
                        for (int x = 0; x < w; x++)
                        {
                            p = pIn;
                            b = pIn[0];
                            g = pIn[1];
                            r = pIn[2];
                            pOut[1] = (byte)g;
                            pOut[2] = (byte)r;
                            pOut[3] = (byte)num;
                            pOut[0] = (byte)b;
                            pIn += 4;
                            pOut += 4;
                        }
                        pIn += srcData.Stride - w * 4;
                        pOut += srcData.Stride - w * 4;
                    }
                    src.UnlockBits(srcData);
                    dstBitmap.UnlockBits(dstData);
                    return dstBitmap;
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 图层叠加
        /// </summary>
        /// <param name="top">顶层图层</param>
        /// <param name="bot">底层图层</param>
        /// <param name="fAlpha">不透明度</param>
        /// <returns></returns>
        public static Bitmap Overlay(Bitmap top, Bitmap bot, float fAlpha)
        {
            int iwidth = bot.Width;
            int iheight = bot.Height;
            Bitmap newImage = new Bitmap(iwidth, iheight, PixelFormat.Format32bppArgb);
            float[][] colormatrix =
            {
                 new float[]{1,0,0,0,0},//代表了R
                 new float[]{0,1,0,0,0},//代表了G
                 new float[]{0,0,1,0,0},//代表了B
                 new float[]{0,0,0,fAlpha,0},//代表了A
                 new float[]{0,0,0,0,1}
            };

            using (Graphics g = Graphics.FromImage(newImage))
            {
                ColorMatrix cm = new ColorMatrix(colormatrix);
                ImageAttributes imageAtt = new ImageAttributes();
                imageAtt.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
                g.DrawImage(bot, new Rectangle(0, 0, iwidth, iheight), 0, 0, iwidth, iheight, GraphicsUnit.Pixel);
                g.DrawImage(top, new Rectangle(0, 0, iwidth, iheight), 0, 0, iwidth, iheight, GraphicsUnit.Pixel, imageAtt);
            }
            return newImage;
        }

        /// <summary>
        /// 滤色合成两张图片
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="percent"></param>
        /// <returns></returns>
        public static Bitmap Screen(Bitmap a, Bitmap b)
        {
            if (a.Size != b.Size)
            {
                return null;
            }

            int iwidth = a.Width;
            int iheight = a.Height;

            Bitmap c = new Bitmap(iwidth, iheight, PixelFormat.Format24bppRgb);

            BitmapData amData = a.LockBits(new Rectangle(0, 0, a.Width, a.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData cmData = c.LockBits(new Rectangle(0, 0, c.Width, c.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            System.IntPtr Scan0A = amData.Scan0;
            System.IntPtr Scan0B = bmData.Scan0;
            System.IntPtr Scan0C = cmData.Scan0;
            unsafe
            {
                byte* pA = (byte*)(void*)Scan0A;
                byte* pB = (byte*)(void*)Scan0B;
                byte* pC = (byte*)(void*)Scan0C;

                int nOffset = stride - b.Width * 3;

                int r_a, g_a, b_a;
                int r_b, g_b, b_b;
                int p0, p1, p2;

                for (int y = 0; y < iheight; ++y)
                {
                    for (int x = 0; x < iwidth; ++x)
                    {
                        b_a = pA[0];
                        g_a = pA[1];
                        r_a = pA[2];

                        b_b = pB[0];
                        g_b = pB[1];
                        r_b = pB[2];

                        //1-(1-a)*(1-b) = a+b-a*b
                        p0 = (b_a + b_b) - (b_a * b_b) / 255;
                        p1 = (g_a + g_b) - (g_a * g_b) / 255;
                        p2 = (r_a + r_b) - (r_a * r_b) / 255;

                        pC[0] = (byte)p0;
                        pC[1] = (byte)p1;
                        pC[2] = (byte)p2;

                        pA += 3;
                        pB += 3;
                        pC += 3;
                    }
                    pA += nOffset;
                    pB += nOffset;
                    pC += nOffset;
                }
            }
            a.UnlockBits(amData);
            b.UnlockBits(bmData);
            c.UnlockBits(cmData);
            return c;
        }

        /// <summary>
        /// 以滤色混合模式将彩色图像和灰度图像合成
        /// </summary>
        /// <param name="srcImag">原图</param>
        /// <param name="percent">不透明度</param>
        /// <returns>合成结果图像</returns>
        public static Bitmap ScreenForGrayAndOrig(Bitmap srcImag, int percent)
        {
            try
            {
                float fp = percent / 100f;
                Bitmap a = srcImag.Clone() as Bitmap;
                Bitmap b = ToGray(srcImag);
                Bitmap c = Screen(a, b);
                float alpha = percent / 100f;
                Bitmap d = Overlay(c, a, alpha);
                a.Dispose();
                b.Dispose();
                c.Dispose();
                a = null;
                b = null;
                c = null;
                return d;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return null;
            }
        }

        /// <summary>
        /// 以正片叠底的形式合成两张图片
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="percent"></param>
        /// <returns></returns>
        public static Bitmap Multiply(Bitmap a, Bitmap b)
        {
            int iwidth = a.Width;
            int iheight = a.Height;

            Bitmap c = new Bitmap(iwidth, iheight, PixelFormat.Format24bppRgb);

            BitmapData amData = a.LockBits(new Rectangle(0, 0, a.Width, a.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData cmData = c.LockBits(new Rectangle(0, 0, c.Width, c.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            System.IntPtr Scan0A = amData.Scan0;
            System.IntPtr Scan0B = bmData.Scan0;
            System.IntPtr Scan0C = cmData.Scan0;
            unsafe
            {
                byte* pA = (byte*)(void*)Scan0A;
                byte* pB = (byte*)(void*)Scan0B;
                byte* pC = (byte*)(void*)Scan0C;

                int nOffset = stride - b.Width * 3;

                int r_a, g_a, b_a;
                int r_b, g_b, b_b;
                int p0, p1, p2;

                for (int y = 0; y < iheight; ++y)
                {
                    for (int x = 0; x < iwidth; ++x)
                    {
                        b_a = pA[0];
                        g_a = pA[1];
                        r_a = pA[2];

                        b_b = pB[0];
                        g_b = pB[1];
                        r_b = pB[2];

                        //a*b/255
                        p0 = b_a * b_b / 255;
                        p1 = g_a * g_b / 255;
                        p2 = r_a * r_b / 255;

                        pC[0] = (byte)p0;
                        pC[1] = (byte)p1;
                        pC[2] = (byte)p2;

                        pA += 3;
                        pB += 3;
                        pC += 3;
                    }
                    pA += nOffset;
                    pB += nOffset;
                    pC += nOffset;
                }
            }
            a.UnlockBits(amData);
            b.UnlockBits(bmData);
            c.UnlockBits(cmData);
            b.Dispose();
            b = null;
            return c;
        }

        /// <summary>
        /// 以正片叠底模式将彩色图像和自己合成
        /// </summary>
        /// <param name="srcImag">原图</param>
        /// <param name="percent">不透明度</param>
        /// <returns>合成结果图像</returns>
        public static Bitmap MultiplyForOrigAndOrig(Bitmap srcImag, int percent)
        {
            try
            {
                float fp = percent / 100f;
                Bitmap a = srcImag.Clone() as Bitmap;
                Bitmap b = srcImag.Clone() as Bitmap;
                Bitmap c = Multiply(a, b);
                float alpha = percent / 100f;
                Bitmap d = Overlay(c, a, alpha);
                a.Dispose();
                b.Dispose();
                c.Dispose();
                a = null;
                b = null;
                c = null;
                return d;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return null;
            }
        }

        /// <summary>
        /// 以综合模式进行图像合成
        /// </summary>
        /// <param name="srcImag">原图</param>
        /// <param name="percent">不透明度</param>
        /// <returns>合成结果图像</returns>
        public static Bitmap OnMultipleMixing(Bitmap srcImag, int percent1, int percent2)
        {
            try
            {
                Bitmap a = ScreenForGrayAndOrig(srcImag, percent1);
                Bitmap b = MultiplyForOrigAndOrig(a, percent2);
                a.Dispose();
                a = null;
                return b;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return null;
            }
        }

        public static Bitmap OnNormalMix(Bitmap srcImg, int percent)
        {
            Bitmap grayImage = ToGrayByCV(srcImg);           
            int iwidth = srcImg.Width;
            int iheight = srcImg.Height;
            float ff = percent / 100.0f;
            float df = (100 - percent) / 100.0f;

            Bitmap mixImage = new Bitmap(iwidth, iheight, PixelFormat.Format24bppRgb);

            BitmapData amData = grayImage.LockBits(new Rectangle(0, 0, iwidth, iheight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData bmData = srcImg.LockBits(new Rectangle(0, 0, iwidth, iheight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData cmData = mixImage.LockBits(new Rectangle(0, 0, iwidth, iheight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = amData.Stride;
            System.IntPtr Scan0A = amData.Scan0;
            System.IntPtr Scan0B = bmData.Scan0;
            System.IntPtr Scan0C = cmData.Scan0;
            unsafe
            {
                byte* pA = (byte*)(void*)Scan0A;
                byte* pB = (byte*)(void*)Scan0B;
                byte* pC = (byte*)(void*)Scan0C;

                int nOffset = stride - iwidth * 3;
                int r_a, g_a, b_a;
                int r_b, g_b, b_b;
                int p0, p1, p2;

                for (int y = 0; y < iheight; ++y)
                {
                    for (int x = 0; x < iwidth; ++x)
                    {
                        b_a = pA[0];
                        g_a = pA[1];
                        r_a = pA[2];

                        b_b = pB[0];
                        g_b = pB[1];
                        r_b = pB[2];

                        //c = n*a + (1-n)*b
                        p0 = (int)(ff * b_a + df * b_b);
                        p1 = (int)(ff * g_a + df * g_b);
                        p2 = (int)(ff * r_a + df * r_b);

                        pC[0] = (byte)p0;
                        pC[1] = (byte)p1;
                        pC[2] = (byte)p2;

                        pA += 3;
                        pB += 3;
                        pC += 3;
                    }
                    pA += nOffset;
                    pB += nOffset;
                    pC += nOffset;
                }
            }
            grayImage.UnlockBits(amData);
            srcImg.UnlockBits(bmData);
            mixImage.UnlockBits(cmData);
            grayImage.Dispose();
            grayImage = null;
            return mixImage;
        }

        public static Bitmap ToGray(Bitmap b)
        {
            int w = b.Width;
            int h = b.Height;

            Bitmap c = new Bitmap(w, h, PixelFormat.Format24bppRgb);

            BitmapData bmData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData cmData = c.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;

            System.IntPtr Scan0 = bmData.Scan0;
            System.IntPtr Scan1 = cmData.Scan0;
            unsafe
            {
                byte* p0 = (byte*)(void*)Scan0;
                byte* pC = (byte*)(void*)Scan1;

                int nOffset = stride - b.Width * 3;
                byte red, green, blue;
                for (int y = 0; y < h; ++y)
                {
                    for (int x = 0; x < w; ++x)
                    {
                        blue = p0[0];
                        green = p0[1];
                        red = p0[2];
                        pC[0] = pC[1] = pC[2] = (byte)(.299 * red + .587 * green + .114 * blue);
                        p0 += 3;
                        pC += 3;
                    }
                    p0 += nOffset;
                    pC += nOffset;
                }
            }

            b.UnlockBits(bmData);
            c.UnlockBits(cmData);

            return c;
        }

    }
}
